#ifndef __PROCESS_POOL_HPP__
#define __PROCESS_POOL_HPP__

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <string>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <cstdio>
#include <functional>
#include "Task.hpp"

const int default_subprocess_num = 5;//默认进程池创建5个信道
using child_callback=std::function<void(int fd)>;

//将我们的信道定义成一个类，或者说一个自定义类
//这个信道是从父进程的角度出发去定义的，因为整个进程池是由父进程来控制的
class Channel
{
public:
    Channel()
    {

    }

    Channel(int fd,pid_t child,std::string name):_fd(fd),_child(child),_name(name)
    {}
    
    ~Channel()
    {}

    //提供一些能够让外部拿到内部私有成员变量的函数
    int Fd()
    {
       return _fd; 
    }
    pid_t Child()
    {
        return _child;
    }
    std::string Name()
    {
        return _name;
    }

    void Close()
    {
        close(_fd);
    }

    int Wait()
    {
        return waitpid(_child,NULL,0);
    }

private:
    int _fd;//父进程写端fd
    pid_t _child;//通信目标子进程pid
    std::string _name;//信道名称
};

class ProcessPool
{
private:
    void _ControlSubProcess(int& index)
    {
        index++;//父进程轮询式地控制每一个子进程(信道)
        index %= _subnum;
        
        //选择好子进程后，选择任务
        int x = rand() % tasks.size();
        
        //将相应的任务发送给子进程，需要有相应的说明
        std::cout << "subprocess " << _channels[index].Child() << " by " << _channels[index].Name() << " 执行相应任务：" << std::endl;
        write(_channels[index].Fd(),&x,sizeof(x));//写一个任务后

        sleep(1);//派发任务间隔1s
    }
public:
    ProcessPool(int subnum = default_subprocess_num):_subnum(subnum)
    {
        srand((unsigned int)time(NULL));//设置随机数种子，以随机选取任务
    }
    ~ProcessPool()
    {
        //不能将父进程关闭信道写端和回收子进程的代码写在这里
        //因为子进程退出时也会执行这个析构
    }

    void InitProcessPool(child_callback cb)
    {
        //创建一个信道->放入循环中，进程池中创建多信道
        for(int i = 0;i < _subnum;i++)
        {
            int pipefd[2] = {0};
            int ret = pipe(pipefd);//先创建管道，再创建子进程，以实现子进程对管道的继承
            if(ret < 0)
            {
                perror("pipe");
                return;
            }
            //创建管道成功
            pid_t pid = fork();
            if(pid < 0)
            {
                perror("fork");
                return;
            }         
            else if(pid == 0)
            {
                //子进程
                //子进程读,关闭写端
                close(pipefd[1]);

                //子进程继承父进程fd_array中的信息后，除第一个子进程外，均会将之前子进程对应信道的写端继承一份
                //我们要将这个额外继承的，完全不需要的写端关闭
                //写端信息记录在哪里？记录在channel中
                for(auto& e : _channels)
                    e.Close();              

                //形成信道后，子进程干什么：由外部传入的回调函数确定
                cb(pipefd[0]);//将相应子进程的读端传入
                
                // std::cout << getpid() << "channel" << i << std::endl;
                exit(0);//父进程完成任务后，就退出，不要去执行父进程的代码
            }

            //父进程写,关闭读端
            close(pipefd[0]);
            std::string name = "channel-" + std::to_string(i);
            _channels.emplace_back(pipefd[1],pid,name);
            // std::cout << "我是父进程，我成功创建信道" << i << std::endl; 
        }
    }

    //进程池初始化完毕后，要设计进程池的控制逻辑
    //进程池有不止一个信道，我们不能总让父进程和一个信道通信，即总让一个子进程去完成任务，我们希望这些子进程能够轮流完成任务
    


    void ControlSubProcess()
    {
        int index = 0;
        while(true)
        {
            _ControlSubProcess(index);
        }
    }


    //这个重载的函数版本可以通过count控制任务数
    void ControlSubProcess(int count)
    {
        if(count <= 0)
            return;
        int index = 0;
        while(count--)
        {
            _ControlSubProcess(index);
        }
    }
    
    void DestroyProcessPool()
    {
        //销毁进程：1. 父进程关闭所有信道新端  2.父进程等待子进程退出
        for(auto& e : _channels)
            e.Close();
        for(auto& e : _channels)
        {
            int pid = e.Wait();
            if(pid > 0)
                std::cout << "父进程等待子进程成功：" << pid << std::endl;
            else
                perror("waitpid");
        }
    }


private:
    std::vector<Channel> _channels;
    int _subnum;//要创建的信道数(由外部传入) 
};


#endif